package com.wave.wavesystem.serve.service.impl;

import cn.hutool.core.date.DateUtil;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.wave.common.constant.TimeFormatConstant;
import com.wave.common.util.Func;
import com.wave.wavesystem.serve.constant.RedisConstant;
import com.wave.wavesystem.serve.mapper.UserSignMapper;
import com.wave.wavesystem.serve.pojo.entity.CurrentUser;
import com.wave.wavesystem.serve.pojo.entity.UserSign;
import com.wave.wavesystem.serve.pojo.query.UserSignQuery;
import com.wave.wavesystem.serve.service.IUserService;
import com.wave.wavesystem.serve.service.IUserSignService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;


@Slf4j
@Service
public class UserSignServiceImpl extends ServiceImpl<UserSignMapper, UserSign> implements IUserSignService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private IUserService iUserService;

    @Override
    public QueryChain<UserSign> getQueryChain(UserSignQuery query) {
        return queryChain();
    }

    @Override
    public boolean sign(CurrentUser currentUser) {
        String yyyyMM = LocalDateTime.now().format(DateTimeFormatter.ofPattern(TimeFormatConstant.YYYY_MM));
        int dayNumber = DateUtil.dayOfMonth(new Date());
        String signKey = RedisConstant.USER_SING(yyyyMM, String.valueOf(currentUser.getId()));

        UserSign userSign = new UserSign();
        userSign.setUserId(currentUser.getId());

        // 先签到
        stringRedisTemplate.opsForValue().setBit(signKey, dayNumber - 1, true);
        // 获取签到 bitmap
        int getPosition = 8;
        while (dayNumber > getPosition) {
            getPosition *= 2;
        }
        List<Long> ops = stringRedisTemplate.opsForValue().bitField(signKey, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(getPosition)).valueAt(0));
        if (Func.isEmpty(ops) || ops.isEmpty()) {
            log.info("WAVE:INFO 获取当前签到用户失败");
            return false;
        }

        Long num = ops.get(0);
        StringBuilder binaryString = new StringBuilder(Long.toBinaryString(num));
        // 处理 前几天没有签到 导致 位图不准确问题
        while (getPosition != binaryString.length()) {
            binaryString.insert(0, "0");
        }

        // 查询是否 有签到记录 本月还没有签到，新增签到记录
        UserSign signRecord = queryChain().eq(UserSign::getUserId, currentUser.getId())
                .eq(UserSign::getYearMonth, yyyyMM).one();
        if (Func.isEmpty(signRecord)) {
            userSign.setYearMonth(yyyyMM);
            userSign.setSignCount(1);
            userSign.setSignLayout(binaryString.toString());
            return this.save(userSign);
        }

        //2、正常签到更新统计，转换位图
        Long bigCount = stringRedisTemplate.execute((RedisCallback<Long>) connection ->
                connection.stringCommands().bitCount(signKey.getBytes(), 0, dayNumber - 1));
        // 位图转换
        userSign.setSignLayout(binaryString.toString());
        userSign.setSignCount(bigCount.intValue());
        return this.updateChain().eq(UserSign::getUserId, userSign.getUserId())
                .eq(UserSign::getYearMonth, yyyyMM)
                .setRaw(UserSign::getSignCount, bigCount).update();
    }

}
